#pragma once

class CShell final :
	public IEngMod,
	public ICalc,
	public ICriticalSection
{
public:
	CShell();
	~CShell();

	/*-IEngMod------------------------------------------------------------------*/
	virtual void OnInit() override;

	/*-ICalc--------------------------------------------------------------------*/
	virtual void OnCalc(uint arg) override;

	/*--------------------------------------------------------------------------*/

	void Execute(const std::string& cmd);
	void LoadScript(const std::string& path);

	void SetSStream(std::stringstream* ss);

	void GetVars(const std::string& name, std::vector<const std::string>& suggestions);
	void GetCmds(const std::string& name, std::vector<const std::string>& suggestions);

	bool ExistsVar(const std::string& name);
	Variable* AddVar(const std::string& name, int val);
	Variable* AddVar(const std::string& name, const std::string& val);
	Variable* AddVar(const std::string& name, float val);
	Variable* GetVar(const std::string& name);
	Variable* GetOrAddVar(const std::string& name);
	std::string ListAllVars();

	template<typename F>
	void RegisterLuaFunction(const std::string& name, F f)
	{
		luabind::module(this->m_luaState) [
		    luabind::def(name.c_str(), f)
		];

		this->Lock(2);
		this->m_functionsVector.push_back(name);
		this->Unlock(2);
	}

	lua_State* GetLuaState() const;

private:
	std::map<const std::string, Variable*> m_variablesMap;	//!<Lista zmiennych
	std::vector<const std::string> m_functionsVector;

	std::queue<std::string> m_commandsQueue;
	void ParseCommand(const std::string& cmd);

	std::stringstream* m_sstream;

	lua_State* m_luaState;
};

namespace sh
{
void logi(const std::string& message);
void logw(const std::string& message);
void loge(const std::string& message);
void logc(const std::string& message);
void exit();
void loadscript(const std::string& message);

void setstring(const std::string& name, const std::string& value);
void setint(const std::string& name, int value);
void setfloat(const std::string& name, float value);
void setbool(const std::string& name, bool value);

const std::string& getstring(const std::string& name);
int getint(const std::string& name);
float getfloat(const std::string& name);
bool getbool(const std::string& name);

float randfloat(float a, float b);
int randint(int a, int b);

void benchmark();
};

extern CShell* g_Shell;